package com.example.base_model.utils

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext

/**
 * @Author jiangKunKun
 * @CreateDate 2025/8/26
 * @Description 基于 Kotlin Flow 的消息队列实现。
 *              支持泛型消息类型、生命周期感知、以及多种背压处理策略。
 * @SpittingGrooves
 */
class FlowMessageQueue<T> @JvmOverloads constructor(
    private val lifecycle: Lifecycle, // 用于生命周期感知，在组件销毁时自动清理资源
    private val intervalMillis: Long = 0L, // 消息处理的最小间隔时间（毫秒），用于控制消息处理频率
    coroutineContext: CoroutineContext = Dispatchers.Main // 协程运行的上下文，默认为主线程
) {
    /**
     * 消息队列通道 - 延迟初始化。用于发送和接收消息。
     */
    private lateinit var messageChannel: Channel<T>

    /**
     * 标记 [messageChannel] 是否已经初始化过。
     */
    private var isChannelInitialized = false

    /**
     * 协程作用域，用于启动和管理消息队列相关的协程。
     * 使用 [SupervisorJob] 确保子协程失败不会影响其他子协程。
     */
    private val scope = CoroutineScope(coroutineContext + SupervisorJob())

    /**
     * 消息处理回调函数。当接收到消息时，会调用此回调进行处理。
     */
    private var messageHandler: ((T) -> Unit)? = null

    /**
     * 背压处理策略。默认为 [BackpressureStrategy.BUFFER]，即缓冲所有消息。
     */
    private var backpressureStrategy: BackpressureStrategy = BackpressureStrategy.BUFFER

    /**
     * 背压处理策略枚举。
     * 定义了当消息生产者发送消息速度快于消费者处理速度时，消息队列应采取的行为。
     */
    enum class BackpressureStrategy {
        /**
         * 缓冲所有消息 - 默认策略。
         * 优点：不丢失任何消息。
         * 缺点：可能导致内存占用过高，如果生产者持续快速发送消息，可能引发 OOM。
         */
        BUFFER,

        /**
         * 丢弃最新的消息。
         * 优点：保持较低内存占用，避免 OOM。
         * 缺点：可能会丢失最新到达的消息。
         */
        DROP_LATEST,

        /**
         * 丢弃最旧的消息。
         * 优点：保持较低内存占用，保留最新消息，避免 OOM。
         * 缺点：可能会丢失较早的、未处理的消息。
         */
        DROP_OLDEST,

        /**
         * 当处理不过来时挂起发送者。
         * 优点：不丢失消息，保持消息顺序。
         * 缺点：可能阻塞发送线程（生产者），从而影响上游性能。
         */
        SUSPEND
    }

    init {
        // 监听生命周期，在 [LifecycleOwner] 销毁时自动清理资源
        lifecycle.addObserver(object : androidx.lifecycle.DefaultLifecycleObserver {
            override fun onDestroy(owner: LifecycleOwner) {
                cleanup() // 在生命周期销毁时调用清理函数
            }
        })
    }

    /**
     * 设置消息处理回调函数。
     * 当消息队列接收到消息时，会调用此 `handler` 函数进行处理。
     * @param handler 接收消息时执行的回调函数。
     */
    fun setMessageHandler(handler: (T) -> Unit) {
        this.messageHandler = handler
        // 确保 Channel 已初始化，如果尚未初始化则进行初始化
        if (!::messageChannel.isInitialized) {
            initializeChannel()
        }
        startProcessing() // 启动消息处理流程
    }

    /**
     * 设置消息队列的背压处理策略。
     * 只能在 `messageChannel` 尚未初始化之前设置，否则会抛出异常。
     * @param strategy 要设置的背压处理策略。
     * @throws IllegalStateException 如果 Channel 已经初始化，则无法更改策略。
     */
    fun setBackpressureStrategy(strategy: BackpressureStrategy) {
        // 只有在 Channel 还未初始化时才能设置策略，因为 Channel 的容量和行为在创建时确定
        if (!isChannelInitialized) {
            this.backpressureStrategy = strategy
        } else {
            // 如果已经初始化，则忽略或者抛出异常，防止运行时更改 Channel 行为
            throw IllegalStateException("Channel already initialized, cannot change strategy")
        }
    }

    /**
     * 初始化消息队列的 [Channel]（延迟初始化）。
     * 根据当前的 [backpressureStrategy] 创建不同类型的 Channel。
     */
    private fun initializeChannel() {
        if (!isChannelInitialized) {
            messageChannel = when (backpressureStrategy) {
                BackpressureStrategy.BUFFER -> Channel(Channel.UNLIMITED) // 无限容量，缓冲所有消息
//                BackpressureStrategy.DROP_LATEST -> Channel(Channel.UNLIMITED) // 虽然是 UNLIMITED，但在 push 时会手动处理丢弃最新
//                BackpressureStrategy.DROP_OLDEST -> Channel(Channel.UNLIMITED) // 类似 DROP_LATEST，在 push 时手动处理丢弃最旧
                BackpressureStrategy.DROP_LATEST -> Channel(Channel.CONFLATED) // 使用 CONFLATED 自动丢弃旧消息
                //  一般建议范围
                //  小规模应用: 10-50
                //  中等规模应用: 50-200
                //  大规模应用: 200-1000
                //2. 影响因素考虑
                //  内存消耗:
                //  每个消息对象都会占用内存
                //  需要考虑消息对象的大小
                //  过大的缓冲区可能导致内存压力
                //  业务场景:
                //UI 更新消息: 通常 10-50 足够
                //  日志消息: 可以设置较大值，如 100-200
                //  高频事件: 根据实际频率调整
                //推荐值
                //  默认推荐: 50 - 适用于大多数场景
                //  安全范围: 20-200 - 根据具体业务调整
                //  避免设置: 超过 1000 的值，除非有特殊需求
                BackpressureStrategy.DROP_OLDEST -> Channel(100) // 有限缓冲，容量为100
                BackpressureStrategy.SUSPEND -> Channel(Channel.RENDEZVOUS) // 集合点通道，发送者和接收者必须同时准备好，否则挂起
            }
            isChannelInitialized = true
        }
    }

    /**
     * 推送消息到队列。
     * 根据当前设置的背压策略将消息发送到 [messageChannel]。
     * @param message 要推送的消息。
     */
    fun push(message: T) {
        // 确保 Channel 已初始化，如果尚未初始化则进行初始化
        if (!::messageChannel.isInitialized) {
            initializeChannel()
        }

        // 只有当 Channel 未关闭发送时才推送消息
        if (!messageChannel.isClosedForSend) {
            scope.launch {
                when (backpressureStrategy) {
                    BackpressureStrategy.BUFFER -> {
                        messageChannel.send(message) // 缓冲所有消息，如果缓冲区满则挂起
                    }
//                    BackpressureStrategy.DROP_LATEST -> {
//                        // 尝试发送消息，如果缓冲区已满，则丢弃最新的消息（即不发送）
//                        messageChannel.trySend(message)
//                    }
//                    BackpressureStrategy.DROP_OLDEST -> {
//                        if (messageChannel.isEmpty) {
//                            // 如果队列为空，直接发送
//                            messageChannel.send(message)
//                        } else {
//                            // 如果队列不为空，移除最旧的消息（头部）并添加新消息（尾部）
//                            messageChannel.tryReceive() // 移除最旧的
//                            messageChannel.send(message) // 添加新的
//                        }
//                    }
                    BackpressureStrategy.DROP_LATEST -> {
                        // 使用 CONFLATED Channel，会自动丢弃旧消息
                        messageChannel.send(message)
                    }
                    BackpressureStrategy.DROP_OLDEST -> {
                        // 使用有限缓冲 Channel，会自动丢弃最旧的消息
                        messageChannel.send(message)
                    }
                    BackpressureStrategy.SUSPEND -> {
                        messageChannel.send(message) // 挂起发送者直到接收者准备好
                    }
                }
            }
        }
    }

    /**
     * 获取 [Flow] 流用于自定义消息处理。
     * 注意：如果使用此方法获取 Flow 进行处理，则不应再使用 [setMessageHandler] 监听消息，因为消息会被 Flow 消耗。
     * @return 消息的 [Flow] 流。
     */
    fun asFlow(): Flow<T> = messageChannel.receiveAsFlow()

    /**
     * 启动消息处理流程。
     * 创建一个协程来持续从 [messageChannel] 收集消息，并根据 `intervalMillis` 控制处理频率。
     */
    private fun startProcessing() {
        // 确保 Channel 已初始化，如果尚未初始化则进行初始化
        if (!::messageChannel.isInitialized) {
            initializeChannel()
        }

        // 启动一个新的协程来处理消息
        scope.launch {
            // 将 Channel 转换为 Flow 以便使用 Flow 的操作符，实现响应式处理
            val flow = messageChannel.receiveAsFlow()

            // 记录上一次处理消息的时间戳（毫秒），用于控制消息处理间隔
            var lastProcessTime = System.currentTimeMillis()

            // 开始收集 Flow 中的消息
            flow.collect { message ->
                // 获取当前时间戳
                val currentTime = System.currentTimeMillis()

                // 计算距离上次处理消息经过的时间（毫秒）
                val timeSinceLastProcess = currentTime - lastProcessTime

                // 判断是否需要延迟处理：
                // 1. 如果经过的时间小于设定的间隔时间
                // 2. 并且间隔时间大于0（即设置了处理间隔）
                if (timeSinceLastProcess < intervalMillis && intervalMillis > 0) {
                    // 计算需要等待的时间 = 设定间隔 - 已经过的时间
                    val delayTime = intervalMillis - timeSinceLastProcess

                    // 延迟指定时间后再处理消息
                    delay(delayTime)
                }

                // 调用设置的消息处理回调函数处理当前消息
                messageHandler?.invoke(message)

                // 更新上次处理时间为我们处理完当前消息的时间
                lastProcessTime = System.currentTimeMillis()
            }
        }
    }

    /**
     * 清理资源。取消协程作用域并关闭消息通道。
     * 在 [LifecycleOwner] 销毁时自动调用。
     */
    private fun cleanup() {
        scope.cancel() // 取消协程作用域中的所有协程
        if (::messageChannel.isInitialized) {
            messageChannel.close() // 关闭消息通道，停止接收新消息
        }
    }

    /**
     * 手动取消和清理资源。
     * 外部可以显式调用此方法来停止消息队列并释放资源。
     */
    fun cancel() {
        cleanup()
    }
}